import { _decorator, Component, Node, Vec3, tween, EventTarget, CCBoolean, Tween } from 'cc';

const { ccclass, property } = _decorator;

@ccclass('PathMove')
export class PathMove extends Component {

    @property(Node)
    public pathRoot: Node = null!; // 路径点的根节点

    @property(Node)
    public target: Node = null!; // 需要沿路径移动的目标物体

    @property
    public movementParameter: number = 5; // 移动速度或总时长

    @property
    public speedBased: boolean = false; // 是否按速度来运动

    @property
    public autoPlay: boolean = false; // 是否自动开始移动

    @property
    public loop: boolean = false; // 是否循环播放

    @property
    public gotoFirstPoint: boolean = false; // 是否自动移动到第一个点

    public onPathComplete: () => void = () => {}; // 整个路径完成的事件

    public onNextPointChanged: (next:Vec3) => void = () => {}; // 整个路径完成的事件
    
    public onPathStart: ((index: number, allPathPoints: Vec3[]) => void) | null = null; // 路径开始事件


    private pathPoints: Vec3[] = []; // 存储路径点

    public onPointComplete: (index:number) => void = (index:number) => {}; // 整个路径完成的事件

    private totalLength:number;
    private pathLengths: number[] = [];

    public nextPoint:Vec3;

    private nodeTween:Tween<Node>;
    
    @property(CCBoolean)
    public isMoving:boolean = false;
    protected onLoad(): void {
    }

    start() {
        if (this.autoPlay) {
            this.setPathRoot(this.pathRoot);
            this.move();
        }
    }
    
    public move(){
        if (this.gotoFirstPoint) {
            this.moveToFirstPoint();
        } else {
            this.moveAlongPath();
        }
        this.isMoving = true;
    }
    
    public setPathRoot(pathRoot:Node)
    {
        this.onPointComplete = null;
        this.onPathStart = null;
        this.onPathComplete = null;
        this.onNextPointChanged = null;
        this.pathRoot = pathRoot;
        this.initializePathPoints(this.castPoints(this.pathRoot));
        this.registerAllEvents();
    }
    
    public setPathPoints(points:Vec3[])
    {
        this.onPointComplete = null;
        this.onPathStart = null;
        this.onPathComplete = null;
        this.onNextPointChanged = null;
        this.initializePathPoints(points);
        this.registerAllEvents();
        this.pathRoot = null;
    }

    private initializePathPoints(points:Vec3[]) {
        this.pathPoints = points; 
        this.pathLengths = [];
        {
            const startPoint = this.target.worldPosition.clone();
            const distanceToFirst = startPoint.subtract(this.pathPoints[0]).length();
            this.pathLengths.push(distanceToFirst);
        }
        
        this.totalLength = 0;

        for (let i = 0; i < this.pathPoints.length - 1; i++) {
            const startPoint = this.pathPoints[i].clone();
            const endPoint = this.pathPoints[i + 1].clone();
            const distance = startPoint.subtract(endPoint).length();
            this.pathLengths.push(distance);
            this.totalLength += distance;
        }
    }

    /**
     * 移动到第一个路径点
     */
    private moveToFirstPoint() {
        if (this.pathPoints.length < 1) {
            console.error("路径点不足以进行移动");
            return;
        }
        if (this.onPathStart) {
            this.onPathStart(0, this.pathPoints);
        }

        const firstPoint = this.pathPoints[0];
        this.target.setWorldPosition(firstPoint);
        this.onPointComplete?.(0);
        const pathPointsCopy = this.pathPoints.slice();
        const pathLengthsCopy = this.pathLengths.slice();
        const totalLengthCopy = this.totalLength;
        this.moveToNextPoint(1, pathPointsCopy, pathLengthsCopy, totalLengthCopy);
    }

    /**
     * 沿路径移动目标物体
     */
    private moveAlongPath() {
        if (this.pathPoints.length < 1) {
            console.error("路径点不足以进行移动");
            return;
        }

        if (this.onPathStart) {
            this.onPathStart(0, this.pathPoints);
        }

        const avgSpeed = this.speedBased
        ? this.movementParameter
        : this.totalLength/this.movementParameter;
        const duration = this.pathLengths[0] / avgSpeed;

        const endPoint = this.pathPoints[0];

        this.nodeTween = tween(this.target)
            .to(duration, { worldPosition: endPoint }, { easing: 'linear' })
            .call(() => {
                this.onPointComplete?.(0);

                const pathPointsCopy = this.pathPoints.slice();
                const pathLengthsCopy = this.pathLengths.slice();
                const totalLengthCopy = this.totalLength;
                this.moveToNextPoint(1, pathPointsCopy, pathLengthsCopy, totalLengthCopy);
            })
            .start();

            this.nextPoint = endPoint.clone();
            this.onNextPointChanged?.(this.nextPoint.clone());
    }

    /**
     * 移动到下一个路径点
     */
    private moveToNextPoint(currentIndex: number, pathPoints: Vec3[], pathLengths: number[], totalLength: number) {
        if (currentIndex >= pathPoints.length) {
            this.isMoving = false;
            this.onPathComplete?.();
            if (this.loop) {
                this.move();
            }
            return;
        }
        this.isMoving = true;
        const endPoint = pathPoints[currentIndex];
        const distance = pathLengths[currentIndex];
        const duration = this.speedBased
            ? distance / this.movementParameter
            : (distance / totalLength) * this.movementParameter;

        this.nodeTween = tween(this.target)
            .to(duration, { worldPosition: endPoint }, { easing: 'linear' })
            .call(() => {
                this.onPointComplete?.(currentIndex);
                this.moveToNextPoint(currentIndex + 1, pathPoints, pathLengths, totalLength);
            })
            .start();

        this.nextPoint = endPoint.clone();
        this.onNextPointChanged?.(this.nextPoint.clone());
    }

    public getPathPoints(): Vec3[] {
        return this.pathPoints.slice(); // 返回路径点的副本，避免外部修改
    }

    public castPoints(pathRoot:Node):Vec3[]{
        return this.pathRoot.children.map(child => child.worldPosition.clone());
    }

    public events:Function[] = [];
    
    registerAllEvents()
    {
        if(this.events != null){
            for (let i = 0; i < this.events.length; i++) {
                this.events[i]();
            }
        }
    }
}
